`timescale 1ns / 1ps
/*-------------------------------------------------------------------------------------------------------*\
FileName        : cbb_fifo.v
Author          ：hpy
Email           ：yuan_hp@qq.com
Date            ：2022年07月28日
Description     ：同步fifo，深度和数据宽度可设置

1.接口
cbb_fifo #(
	.de(de)，  //数据深度，任意正整数，若不是2的幂，会向上自动取2的幂次值，如设置为3，那么实际深度为4
    .dw(dw)    //数据宽度，
)u_cbb_fifo (
	.clk(clk),    //系统时候时钟
	.rst_n(rst_n), //复位信后 ，低有效
	.clr(1'b0),    // fifo清空，高有效
	.din(din),    //输入的数据
	.we(we),     //写使能 ， 高有效
	.re(re),     //读使能，若非空，先读数据然后使能一次，则数据出队
    .dout(dout), //读出的数据
    .full() ,    //fifo满则为1 
    .empty()     //fifo空则为1
);
\*--------------------------------------------------------------------------------------------------------*/

// synopsys translate_on

module cbb_fifo#(
    parameter dw =8  ,//数据宽度
    parameter de =4   //数据深度，verilog根据设置值，自动向上修正为2^n值
)(clk, rst_n, clr,  din, we, dout, re, full, empty);

function integer clog2(input integer i);
    for(clog2=0;i>0 ; clog2=clog2+1)
        i=i>>1; 
endfunction 

localparam dew = clog2(de-1) ;  // deep width 
localparam der = 1<<dew ; //deep real 

input		clk, rst_n;
input		clr;
input   [dw:1]	din;
input		we;
output  [dw:1]	dout;
input		re;
output		full, empty;


////////////////////////////////////////////////////////////////////
//
// Local Wires
//

reg     [dw:1]	mem[0:der-1];
wire [7:0] mder , mdew ; 
assign  mder = der ;
assign mdew = dew;
reg     [dew-1:0]   wp;
reg     [dew-1:0]   rp;
wire    [dew-1:0]   wp_p1;
wire    [dew-1:0]   wp_p2;
wire    [dew-1:0]   rp_p1;
wire		full, empty;
reg		gb;
reg [dew:0] cnt ; 
////////////////////////////////////////////////////////////////////
//
// Misc Logic
//

always @(posedge clk or negedge rst_n)
        if(!rst_n)	wp <= #0 {dew{1'b0}};
        else
        if(clr)		wp <= #0 {dew{1'b0}};
        else
        if(we)		wp <= #0 wp_p1;

assign wp_p1 = wp + 1'h1;
// assign wp_p2 = wp + 2'h2;

always @(posedge clk or negedge rst_n)
        if(!rst_n)	rp <= #0 {dew{1'b0}};
        else
        if(clr)		rp <= #0 {dew{1'b0}};
        else
        if(re)		rp <= #0 rp_p1;

assign rp_p1 = rp + 1'h1;

// Fifo Output
assign  dout = mem[ rp[dew-1:0] ];

// Fifo Input
always @(posedge clk)
        if(we)	mem[ wp[dew-1:0] ] <= #0 din;


always @(posedge clk) begin
    if(rst_n==1'b0 | clr==1'b1) 
        cnt <= {dew{1'b0}} ;
    else if(we & ~re & !full)
        cnt <= cnt + 1'b1 ; 
    else if(~we & re & ~empty)
        cnt <= cnt - 1'b1;
    else
        cnt <= cnt ;

end

// Status
// assign empty = ((wp == rp))  ;//|  (rp_p1 == wp & re) ;// !gb;
// assign full  =  ((wp_p1 == rp ) & we) | ((wp==rp) & ~we & gb);

assign empty = (cnt==0) ? 1'b1 : 1'b0;//|  (rp_p1 == wp & re) ;// !gb;
assign full  = (cnt==der) ? 1'b1: 1'b0 ;// ((wp_p1 == rp ) & we) | ((wp==rp) & ~we & gb);

// Guard Bit ...
/*
always @(posedge clk)
	if(!rst_n)			gb <= #0 1'b0;
	else
	if(clr)				gb <= #0 1'b0;
	else
	if((wp_p1 == rp) & we)		gb <= #0 1'b1;
	else
	if (re)				gb <= #0 1'b0;
*/
endmodule
